#pragma once
#include <vector>
#include <semaphore.h>
#include <pthread.h>
template <typename T>
class RingQueue
{
private:
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void UnLock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }

public:
    RingQueue(int cap)
        : _rq(cap), _cap(cap), _productor_step(0), _consumer_step(0)
    {
        // 初始化信号量
        sem_init(&_room_sem, 0, cap); // 信号量是线程间共享
        sem_init(&_data_sem, 0, 0);
        // 初始化互斥量
        pthread_mutex_init(&_productor_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }
    ~RingQueue()
    {
        // 释放信号量
        sem_destroy(&_room_sem);
        sem_destroy(&_data_sem);
        // 释放互斥量
        pthread_mutex_destroy(&_productor_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }
    void push(T &val) // 生产者接口 申请room信号量 释放data信号量
    {
        // LockQueue(_productor_mutex);
        P(_room_sem);           // 信号量的申请是原子的，可以并发进行
        Lock(_productor_mutex); // 将信号量申请好之后再排队，这样只要有锁了就可以直接生产，不需要再申请信号量(类比先买票在排队)
        // 申请空间成功，开始生产
        _rq[_productor_step++] = val;
        _productor_step %= _cap;
        UnLock(_productor_mutex);
        V(_data_sem); // 信号量释放是原子的，可以并发
    }
    void pop(T &val) // 消费者接口 申请data信号量 释放room信号量
    {
        P(_data_sem);
        Lock(_consumer_mutex);
        // 申请数据成功，开始消耗
        val = _rq[_consumer_step++];
        _consumer_step %= _cap;
        UnLock(_consumer_mutex);
        V(_room_sem);
    }
    int GetAllSem()
    {
        int pro_sem, con_sem;
        sem_getvalue(&_room_sem, &pro_sem);
        sem_getvalue(&_data_sem, &con_sem);
        return pro_sem + con_sem;
    }

public:
    // 底层结构
    std::vector<T> _rq;
    int _cap;
    // 生产者消费者下标
    int _productor_step;
    int _consumer_step;
    // 信号量(预定资源[队列空间])
    sem_t _room_sem; // 生产者关心
    sem_t _data_sem; // 消费者关心
    // 互斥量(保证多生产多消费时不会出现线程安全)
    pthread_mutex_t _productor_mutex; // 一个生产者生产时，防止其他生产者也生产    需要保证生产是按顺序的，不能同时生产，生产时访问了公共资源consumer_step
    pthread_mutex_t _consumer_mutex;  // 一个消费者消费时，防止其他消费者也消费    需要保证消费是按顺序的，不能同时消费，消费时访问了公共资源productor_step
};